classdef Bank
    % Bank Class 
    properties
        ProjPayoff  = 2.5   % Payoff of project at t=2 if successful ("z")
        Liquidation = 0.5   % Liquidation value at t=1 ("ell")
        FaceValue   = 1.5   % Face value of debt contract at t=2 ("F")
        LiqRatio    = 0.9   % Liquidity ratio ("lambda")
        ResRatio    = 0.2   % HQLA-to-deposit ratio ("rho")
        FlagMaxFaceValue = true % If this varable is set to true, the bank sets the
                                % face value equal to its optimal value "F upper bar"
                                % (see Corollary 1 in section 3.2.2)
    end
    
    properties (Dependent, SetAccess = private)
        MassDefaultHigh     % Mass of running creditor above which bank default at t=1 ("n upper bar")
        MassDefaultLow      % Mass of running creditor below which bank default at t=1 ("n lower bar")
        qDebt               % Quantity of debt issued by bank ("delta")
        qBond               % Quantity of government bonds held by bank ("beta")
        MaxFaceValue        % Maximal face value that the bank can promise to pay to creditors ("F upper bar")
        RecoveryNoRun       % Recovered value per unit of issued debt if project succeeds ("F hat")
        ProbEfficient       % Cutoff level for the success probability of project
                            % below which liquidation of the project is efficient("pE")
    end
    
    methods
        function pE = get.ProbEfficient(obj)
           % Efficient success probability of project (eq. 1)
           pE = obj.Liquidation/obj.ProjPayoff;
        end

        function F_max = get.MaxFaceValue(obj)
           % Maximal face value that can be credibly promised by the bank (eq. 10)
           F_max = obj.ProjPayoff/(obj.qDebt-obj.qBond);
        end  
        
        function F0 = get.RecoveryNoRun(obj)
           % Available resources per unit of issued debt (eq. 11)
           F0 = (obj.ProjPayoff+obj.qBond)/obj.qDebt;
        end
        
        function nH = get.MassDefaultHigh(obj)
            % Mass of running creditor above which bank default at t=1 (eq. 41)
            if obj.FaceValue>=obj.MaxFaceValue
                nH = obj.ResRatio;
            else
                nH = (obj.LiqRatio-obj.ProbEfficient*obj.FaceValue)...
                    /(1-obj.ProbEfficient*obj.FaceValue);
            end           
        end

        function nL = get.MassDefaultLow(obj)
            % Mass of running creditor below which bank default at t=1 (eq. 42)
            if obj.FaceValue>=obj.MaxFaceValue
                nL = obj.ResRatio;
            else
                nL = max(0,(obj.FaceValue-obj.RecoveryNoRun)/(obj.FaceValue-1));
            end
        end
        
        function d = get.qDebt(obj)
           % Given the bank's liquidity ratio and HQLA-to-deposit ratio, 
           % this is the quantity of deposit it issued
            d = obj.Liquidation/(obj.LiqRatio-obj.ResRatio);
        end
        
        function b = get.qBond(obj)
           % Given the bank's liquidity ratio and HQLA-to-deposit ratio, 
           % this is the quantity of deposit it issued
           b = obj.Liquidation*obj.ResRatio/(obj.LiqRatio-obj.ResRatio);
        end
        
        function [D1,D2_0,D2_z] = getDebtPayoff(obj,MassWithdraw)
            % This function returns the payoff of the bank's debt contract
            % conditional on the mass of running creditors

            % Auxiliary variables
            LR  = obj.LiqRatio;
            RR  = obj.ResRatio;
            d   = obj.qDebt;
            b   = obj.qBond;
            F   = obj.FaceValue;
            z   = obj.ProjPayoff;
            ell = obj.Liquidation;
            n   = MassWithdraw;
            
            % Payoff at t=1 (eq. 6)
            idx         = logical(n>LR);
            D1          = ones(length(n),1);
            D1(idx)     = LR./n(idx);
            
            % Payoff at t=2 if project fails (eq. 8-9 with Z=0)
            idx         = logical(n<=RR);
            D2_0        = zeros(length(n),1);
            D2_0(idx)   = (b-n(idx)*d)./((1-n(idx))*d);
            
            % Payoff at t=2 if project succeeds (eq. 8-9 with Z=z)
            idx         = logical(n<LR);
            D2_z        = zeros(length(n),1);
            D2_z(idx)   = ((1-max(0,d*n(idx)-b)/ell)*z+max(0,b-d*n(idx)))./((1-n(idx))*d);
            D2_z        = min(F,D2_z);
            
        end
        
        function [IntD1,IntD20,IntD2z] = getDebtPayoffLaplacian(obj)
            % This function returns the expected payoff of the bank's debt contract
            % under the Laplacian belief

            % Auxiliary variables
            LR  = obj.LiqRatio;
            RR  = obj.ResRatio;  
            F   = obj.FaceValue;
            F0  = obj.RecoveryNoRun;
            pE  = obj.ProbEfficient;
            nL  = obj.MassDefaultLow;
            nH  = obj.MassDefaultHigh;
            
            % Expected payoff of withdrawing early (eq. 52)
            if LR>0
                IntD1 = obj.g1(LR);
            else
                IntD1 = 0;
            end

            % Expected payoff of staying if projet fails (eq. 53)
            if RR<1
                IntD20 = obj.g2(RR);
            else
                IntD20 = 1;
            end
            
            % Expected payoff of staying if projet succeeds (eq. 54)
            IntD2z = nL-(F0-1)*log(1-nL)+F*(nH-nL)...
                +(LR-nH-(1-LR)*(log(1-nH)-log(1-LR)))/pE; 
        end
        
        function [pR,DpR_d,DpR_b] = getProjProbAtRun(obj,varargin)
            % This function calculates the project's success probability
            % at the run threshold 
            
            % Inputs
            p = inputParser;
            p.addParameter('LiqRatio',double.empty,@(x)isnumeric(x)&&isscalar(x));
            p.addParameter('ResRatio',double.empty,@(x)isnumeric(x)&&isscalar(x));
            p.parse(varargin{:});
            
            LR = p.Results.LiqRatio;
            RR = p.Results.ResRatio;
            
            if ~isempty(LR); obj.LiqRatio=LR; end
            if ~isempty(RR); obj.ResRatio=RR; end
            
            % Auxiliary variables
            LR  = obj.LiqRatio;
            RR  = obj.ResRatio;
            d   = obj.qDebt;
            F   = obj.FaceValue;
            F0  = obj.RecoveryNoRun;
            FM  = obj.MaxFaceValue;
            nL  = obj.MassDefaultLow;
            nH  = obj.MassDefaultHigh;
            pE  = obj.ProbEfficient;
            
            % Partial derivatives of payoff of debt contract
            DnH_LR = 1/(1-pE*F);
            if nL>0
                DnL_F0 = -1/(F-1); 
            else
                DnL_F0 = 0; 
            end
            DF0_LR = 1/pE;
            DF0_RR = 1-1/pE;
            
            dintD2z_F0 = -log(1-nL);
            dintD2z_nL = 1+(F0-1)/(1-nL)-F;
            dintD2z_nH = F+((1-LR)/(1-nH)-1)/pE;
            dintD2z_LR = log((1-nH)/(1-LR));
            
            DintD1_LR   = -log(LR);
            DintD1_RR   = 0;
            DintD20_LR  = 0;
            DintD20_RR  = -log(1-RR);
            DintD2z_LR  = dintD2z_LR+(dintD2z_F0+dintD2z_nL*DnL_F0)*DF0_LR+dintD2z_nH*DnH_LR;
            DintD2z_RR  = (dintD2z_F0+dintD2z_nL*DnL_F0)*DF0_RR;

            % Project's success probability at run threshold
            % (including its partial derivatives w.r.t. lambda & rho)

            % General case in the appendix (F < FM): eq. 43
            if F<FM&&~obj.FlagMaxFaceValue
                [IntD1,IntD20,IntD2z] = getDebtPayoffLaplacian(obj);
                pR = (IntD1-IntD20)/(IntD2z-IntD20);

                DpR_LR = ((DintD1_LR-DintD20_LR)*(IntD2z-IntD20)...
                    -(IntD1-IntD20)*(DintD2z_LR-DintD20_LR))...
                    /(IntD2z-IntD20)^2;
                DpR_RR = ((DintD1_RR-DintD20_RR)*(IntD2z-IntD20)...
                    -(IntD1-IntD20)*(DintD2z_RR-DintD20_RR))...
                    /(IntD2z-IntD20)^2;

            % Special case in the main text (F = FM): eq. 21 
            else
                den = obj.g2(LR)-obj.g2(RR);
                Gam = (obj.g1(LR)-obj.g2(RR))/den;
                pR  = pE*Gam;

                DpR_LR  = pE*(Gam*log(1-LR)-log(LR))/den;
                DpR_RR  = -pE*(Gam-1)*log(1-RR)/den;
            end
            
            % Partial derivatives of project's success probability w.r.t. d & b
            DLR_d =-LR/d;
            DRR_d =-RR/d;
            DLR_b =1/d;
            DRR_b =1/d;

            DpR_d = DpR_LR*DLR_d+DpR_RR*DRR_d;
            DpR_b = DpR_LR*DLR_b+DpR_RR*DRR_b;
            
        end
        
        function [LR,outoptim] = getIsoLiqRatio(obj,TargetProjProbAtRun,varargin)
            % This function generates the iso-risk curve in (rho, lambda)-space

            % Inputs
            p = inputParser;
            p.addParameter('x0',[-6,6],@(x)isnumeric(x)&&isscalar(x));
            p.addParameter('TolX',10e-12,@(x)isnumeric(x)&&isscalar(x));
            p.addParameter('TolFun',10e-12,@(x)isnumeric(x)&&isscalar(x));
            p.parse(varargin{:});
            
            x0      = p.Results.x0;
            TolX    = p.Results.TolX;
            TolFun  = p.Results.TolFun;
            
            RR = obj.ResRatio;

            % Numerically solve for root of function
            options = optimset('TolX',TolX,'TolFun',TolFun,'Display','None');
            map =  @(y)RR+(1-RR)/(1+exp(-y));
            fun = @(x)(obj.getProjProbAtRun('LiqRatio',map(x),'ResRatio',RR)-TargetProjProbAtRun);
            [xs,~,~,outoptim] = fzero(fun,x0,options);
            LR = map(xs);
            
        end
    end

    methods (Static)
        function x = g1(x)
            % Auxiliary function to compute run threshold (eq. 22)
            x = x-x.*log(x);
        end

        function x = g2(x)
            % Auxiliary function to compute run threshold (eq. 22)
            x = 1-Bank.g1(1-x);
        end
    end
    
end